﻿/*
 * 作者：新生命开发团队（http://www.newlifex.com/）
 * 
 * 版权：版权所有 (C) 新生命开发团队 2002-2014
 * 
 * 修改：海洋饼干（cuteant@outlook.com）
*/
using System;
using System.Collections.Generic;

namespace CuteAnt.Common
{
  /// <summary>身份证</summary>
  public class IdentityCard
  {
    #region 属性
    /// <summary>生日</summary>
    public DateTime Birthday { get; set; }

    /// <summary>性别</summary>
    public enum SexType
    {
      /// <summary>男</summary>
      Man,

      /// <summary>女</summary>
      Woman
    }

    /// <summary>性别</summary>
    public SexType Sex { get; set; }

    /// <summary>是否15位旧卡</summary>
    public Boolean IsOld { get; set; }

    /// <summary>地区编码</summary>
    public String AreaNum { get; set; }
    #endregion

    #region 构造函数
    ///// <summary>初始化</summary>
    //public IdentityCard() { }
    #endregion

    #region 验证
    /// <summary>验证身份证是否合法</summary>
    /// <param name="card"></param>
    /// <returns></returns>
    public static Boolean Valid(String card)
    {
      try
      {
        if (Parse(card) != null) return true;
      }
      catch { }

      return false;
    }
    #endregion

    #region 分析
    private static Dictionary<String, IdentityCard> cache = new Dictionary<String, IdentityCard>();

    /// <summary>使用身份证号码初始化</summary>
    /// <param name="card"></param>
    /// <returns></returns>
    public static IdentityCard Parse(String card)
    {
      if (String.IsNullOrEmpty(card)) return null;
      if (card.Length != 15 && card.Length != 18) return null;

      //转为小写，18位身份证后面有个字母x
      card = card.ToLower();

      IdentityCard ic = null;
      if (cache.TryGetValue(card, out ic)) return ic;
      lock (cache)
      {
        if (cache.TryGetValue(card, out ic)) return ic;

        var idc = Parse2(card);
        cache.Add(card, idc);
        return idc;
      }
    }

    private static IdentityCard Parse2(String card)
    {
      var area = card.Substring(0, 6);

      var idc = new IdentityCard();
      idc.AreaNum = ParseArea(area);

      if (card.Length == 15)
        idc.ParseBirthdayAndSex15(card);
      else if (card.Length == 18)
      {
        idc.ParseBirthdayAndSex18(card);

        //校验码验证  GB11643-1999标准
        String[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(',');
        Int32[] Wi = new Int32[] { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
        char[] Ai = card.Remove(17).ToCharArray();
        int sum = 0;
        for (int i = 0; i < 17; i++)
        {
          sum += Wi[i] * (Ai[i] - '0');
        }

        int y = -1;
        Math.DivRem(sum, 11, out y);
        if (arrVarifyCode[y] != card.Substring(17, 1).ToLower())
          throw new Exception("验证码校验失败！");
      }

      return idc;
    }
    #endregion

    #region 分析地区
    private static List<Int32> ads = new List<Int32>(new Int32[] { 11, 22, 35, 44, 53, 12, 23, 36, 45, 54, 13, 31, 37, 46, 61, 14, 32, 41, 50, 62, 15, 33, 42, 51, 63, 21, 34, 43, 52, 64, 65, 71, 81, 82, 91 });

    private static String ParseArea(String area)
    {
      Int32 n = 0;
      if (!Int32.TryParse(area, out n)) throw new Exception("非法地区编码！");

      String str = area.Substring(0, 2);
      if (!Int32.TryParse(str, out n)) throw new Exception("非法省份编码！");

      if (!ads.Contains(n)) throw new Exception("没有找到该省份！");

      return area;
    }
    #endregion

    #region 分析生日、性别
    private void ParseBirthdayAndSex15(String card)
    {
      String str = card.Substring(6, 2);
      Int32 n = Int32.Parse(str);

      if (n < 20)
        n = 20;
      else
        n = 19;

      String birth = n.ToString() + card.Substring(6, 6).Insert(2, "-").Insert(5, "-");
      DateTime d = DateTime.MinValue;
      if (!DateTime.TryParse(birth, out d)) throw new Exception("生日不正确！");
      Birthday = d;

      //最后一位是性别
      n = Convert.ToInt32(card.Substring(card.Length - 1, 1));
      Boolean man = n % 2 != 0;

      if (man)
        Sex = SexType.Man;
      else
        Sex = SexType.Woman;
    }

    private void ParseBirthdayAndSex18(String card)
    {
      String birth = card.Substring(6, 8).Insert(4, "-").Insert(7, "-");
      DateTime d = DateTime.MinValue;
      if (!DateTime.TryParse(birth, out d)) throw new Exception("生日不正确！");
      Birthday = d;

      //倒数第二位是性别
      Int32 n = Convert.ToInt32(card.Substring(card.Length - 2, 1));
      Boolean man = n % 2 != 0;

      if (man)
        Sex = SexType.Man;
      else
        Sex = SexType.Woman;
    }
    #endregion
  }
}